package com.google.code.openperfmon.service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.GenericJDBCException;

import com.google.code.openperfmon.dao.MonitorDefinitionDAO;
import com.google.code.openperfmon.dao.MonitorInstanceDAO;
import com.google.code.openperfmon.domain.MonitorDefinition;

public class MonitorDefinitionService {
	public static final String DEFAULT_TEST_HOST = "localhost";
	private static Logger logger = Logger.getLogger(MonitorDefinitionService.class);
	private MonitorDefinitionDAO monitorDefinitionDAO;
	private MonitorInstanceDAO monitorInstanceDAO;
	
	public MonitorDefinitionService(MonitorDefinitionDAO defDAO, MonitorInstanceDAO instDAO) {
		super();
		this.monitorDefinitionDAO = defDAO;
		this.monitorInstanceDAO = instDAO;
	}
	
	public List<AdminEntityInfo<MonitorDefinition>> listMonitorDefinitions() {
		return listMonitorDefinitions(0, Integer.MAX_VALUE);
	}
	
	public MonitorDefinition getById(Long id) {
		return monitorDefinitionDAO.getById(id);
	}
	
	public List<AdminEntityInfo<MonitorDefinition>> listMonitorDefinitions(int start, int count) {
		List<MonitorDefinition> defs = monitorDefinitionDAO.list(start, count);
		if (defs.isEmpty()) return Collections.emptyList();
		
		Collections.sort(defs);
		Map<Long, Integer> cntById = monitorInstanceDAO.getInstanceCountsByDefinitionIds(monitorDefinitionDAO.identities(defs));
		List<AdminEntityInfo<MonitorDefinition>> inf = new ArrayList<AdminEntityInfo<MonitorDefinition>>(defs.size());
		for (MonitorDefinition def : defs) {
			Integer n = cntById.get(def.getId());
			int cnt = (n == null) ? 0 : n.intValue();
			AdminEntityInfo<MonitorDefinition> aei = new AdminEntityInfo<MonitorDefinition>(def, cnt == 0);
			aei.setRefCount(cnt);
			inf.add(aei);
		}
		
		return inf;
	}
	
	public void deleteMonitorDefinition(Long id) throws ReferenceExistException {
		MonitorDefinition def = monitorDefinitionDAO.getById(id);
		try {
			monitorDefinitionDAO.remove(def);
		}
		catch (GenericJDBCException e) {
			Map<Long, Integer> cntById = monitorInstanceDAO.getInstanceCountsByDefinitionIds(Collections.singletonList(id));
			if (cntById.size() > 0) {
				String msg = "Can't delete monitor definition named '" + def.getName() + 
					"' because " + cntById.get(id) + " instances exist.";
				logger.error(msg, e);
				throw new ReferenceExistException(msg, e);
			}
			String msg = "Can't delete monitor definition named '" + def.getName() + 
				" due to unknown reason";
			logger.error(msg, e);
			throw new IllegalStateException(msg, e);
		}
	}
	
	public void saveMonitorDefinition(MonitorDefinition def) throws NonUniqueFieldException {
		try {
			monitorDefinitionDAO.save(def);
		}
		catch (ConstraintViolationException e) {
			MonitorDefinition existing = monitorDefinitionDAO.getByName(def.getName());
			if (existing == null) {
				String msg = "Can't save monitor definition named '" + def.getName() + "' due to unknown reason";
				logger.error(msg, e);
				throw new IllegalStateException(msg, e);
			}
			else {
				throw new NonUniqueFieldException("Monitor definition named '" + def.getName() + "' already exist", e, "name"); 
			}
		}
	}
}
